﻿using IRM.Messaging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
namespace IRM.Messaging.Test
{
    
    
    /// <summary>
    ///This is a test class for MessageTest and is intended
    ///to contain all MessageTest Unit Tests
    ///</summary>
    [TestClass()]
    public class MessageQueueTest
    {


        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        // 
        //You can use the following additional attributes as you write your tests:
        //
        //Use ClassInitialize to run code before running the first test in the class
        //[ClassInitialize()]
        //public static void MyClassInitialize(TestContext testContext)
        //{
        //}
        //
        //Use ClassCleanup to run code after all tests in a class have run
        //[ClassCleanup()]
        //public static void MyClassCleanup()
        //{
        //}
        //
        //Use TestInitialize to run code before running each test
        //[TestInitialize()]
        //public void MyTestInitialize()
        //{
        //}
        //
        //Use TestCleanup to run code after each test has run
        //[TestCleanup()]
        //public void MyTestCleanup()
        //{
        //}
        //
        #endregion


        [TestMethod()]
        public void SHOULD_Have_Added_Status_WHEN_Message_Is_Added_To_Queue()
        {
            MessageQueue queue = GetQueue();
            Message message = new Message();

            queue.Add(message);

            Assert.AreEqual(QueueStatus.Added, message.Status);
        }

        [TestMethod()]
        public void SHOULD_Be_Saved_Once_WHEN_Adding_Multiple_Messages()
        {
            int queueSaved = 0;
            MessageQueue queue = GetQueue((object obj, EventArgs e) => queueSaved++);

            queue.AddRange(new Message(), new Message(), new Message());

            Assert.AreEqual(3, queue.NoOfNotProcessedMessages);
            Assert.AreEqual(1, queueSaved);
        }

        [TestMethod()]
        public void SHOULD_Get_Added_Message_WHEN_Peeked_From_Queue()
        {
            MessageQueue queue = GetQueue();
            Message message = new Message();

            queue.Add(message);

            message = queue.Peek();

            Assert.IsNotNull(message);
        }

        [TestMethod()]
        public void SHOULD_Get_Messages_In_Correct_Order_WHEN_Using_Peek()
        {
            //Correct order is first in, first out

            MessageQueue queue = GetQueueWithTreeMessages();
            Message message;

            message = queue.Peek();
            Assert.AreEqual("Test1", message.Topic);

            message = queue.Peek();
            Assert.AreEqual("Test2", message.Topic);
        }

        [TestMethod()]
        public void SHOULD_Get_Rescheduled_Message_WHEN_RescheduleInterval_Is_Reached()
        {
            //Configed to reschedule 1 second later

            MessageQueue queue = GetQueueWithTreeMessages();
            Message message = queue.Peek();

            queue.Reschedule(message, new Exception());

            message = queue.Peek();
            Assert.IsNull(message);

            System.Threading.Thread.Sleep(1100);

            message = queue.Peek();

            Assert.IsNotNull(message);
            Assert.AreEqual("Test1", message.Topic);
        }

        [TestMethod()]
        public void SHOULD_Not_Get_Rescheduled_Message_WHEN_RescheduleInterval_Is_Not_Reached()
        {
            //Always First in, first out, meaning that rescheduled messages will hold the rest of the queue

            MessageQueue queue = GetQueueWithTreeMessages();
            Message message = queue.Peek();

            queue.Reschedule(message, new Exception());

            message = queue.Peek();

            Assert.IsNull(message);
        }

        [TestMethod()]
        public void SHOULD_Have_Processing_Status_WHEN_Message_Is_Peeked_From_Queue()
        {
            MessageQueue queue = GetQueue();
            Message message = new Message();

            queue.Add(message);

            message = queue.Peek();

            Assert.AreEqual(QueueStatus.Processesing, message.Status);
        }

        [TestMethod()]
        [ExpectedException(typeof(InvalidOperationException))]
        public void SHOULD_Only_Be_Able_To_Remove_WHEN_Message_Status_Is_Processesing()
        {
            MessageQueue queue = GetQueue();
            Message message = new Message();

            queue.Remove(message);
        }

        [TestMethod()]
        public void SHOULD_Have_NoOfNotProcessedMessages_Decreased_WHEN_Message_Is_Removed_From_Queue()
        {
            MessageQueue queue = GetQueueWithTreeMessages();
            Message message = queue.Peek();

            Assert.AreEqual(3, queue.NoOfNotProcessedMessages);

            queue.Remove(message);

            Assert.AreEqual(2, queue.NoOfNotProcessedMessages);
        }

        [TestMethod()]
        public void SHOULD_Have_Processed_Status_WHEN_Message_Is_Removed_From_Queue()
        {
            MessageQueue queue = GetQueueWithTreeMessages();
            Message message = queue.Peek();

            queue.Remove(message);

            Assert.AreEqual(QueueStatus.Processed, message.Status);
        }

        [TestMethod()]
        public void SHOULD_Be_Able_To_Reschedule_WHEN_Message_Status_Is_Processesing()
        {
            MessageQueue queue = GetQueue();
            Message message = new Message();

            queue.Add(message);
            message = queue.Peek();

            queue.Reschedule(message, new Exception());
        }

        [TestMethod()]
        [ExpectedException(typeof(InvalidOperationException))]
        public void SHOULD_Only_Be_Able_To_Reschedule_WHEN_Message_Status_Is_Processesing()
        {
            MessageQueue queue = GetQueue();
            Message message = new Message();

            queue.Reschedule(message, new Exception());
        }

        [TestMethod()]
        public void SHOULD_Have_Rescheduled_Status_WHEN_Message_Is_Rescheduled_To_Queue()
        {
            MessageQueue queue = GetQueue();
            Message message = new Message();

            queue.Add(message);
            message = queue.Peek();

            queue.Reschedule(message, new Exception());

            Assert.AreEqual(QueueStatus.Rescheduled, message.Status);
        }

        [TestMethod()]
        [ExpectedException(typeof(ArgumentNullException))]
        public void SHOULD_Require_Exception_WHEN_Rescheduling()
        {
            MessageQueue queue = GetQueue();

            queue.Reschedule(new Message(), null);
        }

        [TestMethod()]
        [ExpectedException(typeof(ArgumentNullException))]
        public void SHOULD_Require_Exception_WHEN_Failing()
        {
            MessageQueue queue = GetQueue();

            queue.Fail(new Message(), null);
        }

        [TestMethod()]
        public void SHOULD_Include_Exception_Information_On_Message_WHEN_Failing()
        {
            MessageQueue queue = GetQueueWithTreeMessages();

            Message message = queue.Peek();
            queue.Fail(message, new Exception("Failed."));

            Assert.AreEqual(QueueStatus.Error, message.Status);
            Assert.IsFalse(string.IsNullOrEmpty(message.Error));
        }

        [TestMethod()]
        public void SHOULD_Fail_Rescheduled_Message_WHEN_RescheduleUntil_Is_Reached()
        {
            MessageQueue queue = GetQueueWithTreeMessages();

            Message message = queue.Peek();
            message.RescheduleUntil = DateTime.Now;

            queue.Reschedule(message, new Exception());

            Assert.AreEqual(QueueStatus.Error, message.Status);
        }

        [TestMethod()]
        public void SHOULD_Throw_Configed_Exception_After_Saved_Message_WHEN_Failing_With_Such_Policy()
        {
            int savedCalled = 0;
            MessageQueue queue = GetQueue((object obj, EventArgs e) => savedCalled++);

            queue.PolicyName = "Test Rethrow Policy";
            queue.Add(new Message());

            Message message = queue.Peek();

            try
            {
                queue.Fail(message, new ApplicationException("Failed"));
            }
            catch (ApplicationException ex)
            {
                Assert.AreEqual(QueueStatus.Error, message.Status);
                Assert.AreEqual(1, savedCalled);
                Assert.AreEqual("Failed", ex.Message);
            }
        }

        private static MessageQueue GetQueueWithTreeMessages()
        {
            MessageQueue queue = GetQueue();
            queue.AddRange(new Message() { Topic = "Test1" }, new Message() { Topic = "Test2" }, new Message() { Topic = "Test3" });
            return queue;
        }

        private static MessageQueue GetQueue()
        {
            return GetQueue(null);
        }

        private static MessageQueue GetQueue(EventHandler e)
        {
            Repository.InMemoryMessagesDataContext context = new IRM.Messaging.Test.Repository.InMemoryMessagesDataContext();
            if (e != null)
                context.SavedChanges += new EventHandler(e);

            MessageQueue queue = new MessageQueue(context);
            queue.PolicyName = "Test Policy";

            return queue;
        }
    }
}
